#include "red_socket.h"

namespace red {
namespace sockets {

Socket::Socket() :
	m_Socket(SOCKET_INVALIDO),
	m_bUtilizable(false) {

	//Es necesario inicializar esta estructura para que se mantengan con ceros
	//las posiciones no seteadas en esta clase.
	memset(&this->m_sockaddr_in, 0, sizeof(this->m_sockaddr_in));
}

Socket::~Socket() {
	this->close();
}

//Si esta enlazado el servidor o conectado el cliente.
void Socket::setUtilizable(bool p_bUtilizable) {

	this->m_Mutex_bUtilizable.lock();
	this->m_bUtilizable = p_bUtilizable;
	this->m_Mutex_bUtilizable.unlock();
}

//Si esta enlazado el servidor o conectado el cliente.
bool Socket::getUtilizable() {

	bool t_b;

	this->m_Mutex_bUtilizable.lock();
	t_b = ((this->m_bUtilizable) && (this->isValid()));
	this->m_Mutex_bUtilizable.unlock();

	return t_b;
}

//Devuelve true si el socket es un socket válido, false en caso contrario.
bool Socket::isValid() {
	return this->m_Socket != SOCKET_INVALIDO;
}

//Si el socket es válido lo cierra, de lo contrario no hace nada.
void Socket::close() {

	this->setUtilizable(false);

	if (this->isValid()) {
		::close(this->m_Socket);
	}
}

//Trata de abrir un socket válido.
bool Socket::create() {

	//El socket está orientado a la conexión.
	this->m_Socket = ::socket(AF_INET, SOCK_STREAM, 0);

	return this->isValid();
}

//Envía datos atraves del socket. Devuelvo true si todo anduvo bien, false en caso contrario.
bool Socket::send(const string &p_sBuffer, unsigned int &p_wCantEnviada) {

	int t_iCantParcial = 0; //Cantidad parcial de bytes leidos en cada iteracción.
	bool t_b = false; //Valor que devolverá esta función.

	p_wCantEnviada = 0; //El total de bytes enviados hasta el momento.

	//Si el socket está abierto y hay algo para enviar intenta hacer el envio.
	if (!this->getUtilizable()) {
	} else if (p_sBuffer.length() > 0) {

		t_b = true; //Hasta ahora no hay ningún error.

		do {

			//Voy enviando de a pedazos.
			t_iCantParcial = ::send(this->m_Socket, p_sBuffer.substr(p_wCantEnviada).data(), 
				p_sBuffer.length() - p_wCantEnviada, 0);

			if (t_iCantParcial <= 0) { //El socket devuelve -1 si hubo algún error.
				this->setUtilizable(false);
				t_b = false;
			} else { //Sino incremento la cantidad de bytes ya enviada.
				p_wCantEnviada += t_iCantParcial;
			}

		} while ((t_b) && (t_iCantParcial > 0) && (p_wCantEnviada < p_sBuffer.length()));
	}

	return t_b;
}

//Recibe datos del del socket. Devuelvo true si todo anduvo bien, false en caso contrario.
bool Socket::recv(string &p_sBuffer, unsigned int p_wCantARecibir, unsigned int &p_wCantRecibida) {

	char *t_sTemp = NULL; //Buffer auxiliar.
	int t_iCantParcial = 0; //Cantidad parcial de bytes recibidos en cada iteracción.
	bool t_b = false; //Valor que devolverá esta función

	p_wCantRecibida = 0; //El total de bytes recibidos hasta el momento.

	//Si el socket está abierto y hay algo que quiera recibir intenta hacer el recibimiento.
	if (!this->getUtilizable()) {
	} else if (p_wCantARecibir > 0) {

		t_sTemp = new char[p_wCantARecibir]; //Construyo un buffer lo suficientemente grande.
		t_b = true; //Hasta ahora no hay ningún error.

		//do {

			//Voy recibiendo de a pedazos.
			t_iCantParcial = ::recv(this->m_Socket, t_sTemp + p_wCantRecibida,
				p_wCantARecibir - p_wCantRecibida, 0);

			if (t_iCantParcial <= 0) { //El socket devuelve -1 si hubo algún error.
				this->setUtilizable(false);
				t_b = false;
			} else { //Sino incremento la cantidad de bytes ya recibida.
				p_wCantRecibida += t_iCantParcial;
			}

		//} while ((t_b) && (t_iCantParcial > 0) && (p_wCantRecibida < p_wCantARecibir));

		p_sBuffer.append(t_sTemp, p_wCantRecibida);
		delete [] t_sTemp;
	}

	return t_b;
}

}
}
